package FSM ( sysFSM, BitParityCounter(..) ) where

import List

--
-- sample FSM
--

interface BitParityCounter =
    reset :: Action
    -- this is because we can't synthesize non-Action inputs:
    nextBit :: Bit 1 -> Action
    count :: Bit 1

data BitParityStates = Zero | One
                     deriving (Eq, Bits)

sysFSM :: Module BitParityCounter
sysFSM =
    module
        -- this is because we can't synthesize inputs:
        bit :: Reg (Bit 1)
        bit <- mkReg 0

        -- Special list syntax would be nice here:
        fsm :: FSM BitParityStates
        fsm <- mkFSM Zero (Cons (FSMRule Zero (bit == 1) One)
                                (Cons (FSMRule One (bit == 1) Zero)
                                    Nil))
        interface
            reset = fsm.reset
            nextBit b = action { bit := b }
            count = pack fsm.state


--
-- FSM construction system
--

{-
-- We could use a struct like this, which is easier to read, but
-- which makes writing rules harder.  Consider:
--    (FSMRule { state = Zero; cond = (bit == 1); newState = One })
-- versus
--    (FSMRule Zero (bit == 1) One)
--
struct FSMRule a =
    state :: a
    cond :: Bool
    newState :: a
--
-- Instead, we use unnamed fields:
-}
data FSMRule a = FSMRule a Bool a

-- At the moment, the output of the FSM is the state.  How could we
-- make the output more general?  Ah!  We could add a field "action"
-- to the table which would be actions performed as part of the rule
-- transition.  For example:
--     data FSMRule a = FSMRule a Bool a Action

interface FSM a =
    reset :: Action
    state :: a

mkFSM :: (Eq a, Bits a sa) => a -> List (FSMRule a) -> Module (FSM a)
mkFSM initial_state rs =
    module
        st :: Reg a
        st <- mkReg initial_state
        addRules $ mkFSMRules st rs
        interface
            reset = action { st := initial_state }
            state = st

mkFSMRules :: (Eq a) => Reg a -> List (FSMRule a) -> Rules
mkFSMRules _ Nil = rules {}
mkFSMRules s (Cons r rs) =
    let
        createRule :: FSMRule a -> Rules
        createRule (FSMRule state cond newstate) =
            rules
                "FSMRule":
                  when s == state, cond
                    ==> action { s := newstate }  -- perform an "action" field here
    in
        createRule r <+> mkFSMRules s rs
